package com.example.springbootlearn.netty.server;

import com.example.springbootlearn.netty.config.NettyConfig;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;

/**
 * @Author：OBM
 * @DATE：2023/2/24 17:12
 */
@Slf4j
@Service
public class SocketServer implements CommandLineRunner {

    private final NettyConfig nettyConfig;
    private final NettyChannelInit nettyChannelInit;
    EventLoopGroup bossGroup;
    EventLoopGroup workerGroup;
    private Channel channel;

    @Autowired
    public SocketServer(NettyConfig nettyConfig, NettyChannelInit nettyChannelInit) {
        this.nettyConfig = nettyConfig;
        this.nettyChannelInit = nettyChannelInit;
        if (Epoll.isAvailable()) {
            this.bossGroup = new EpollEventLoopGroup(nettyConfig.getBossThread());
            this.workerGroup = new EpollEventLoopGroup(nettyConfig.getWorkerThread());
        } else {
            this.bossGroup = new NioEventLoopGroup(nettyConfig.getBossThread());
            this.workerGroup = new NioEventLoopGroup(nettyConfig.getWorkerThread());
        }

    }

    @Override
    public void run(String... args) {
        try {
            boolean useEpoll = Epoll.isAvailable();
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup);
            if (useEpoll) {
                bootstrap.channel(EpollServerSocketChannel.class);
            } else {
                bootstrap.channel(NioServerSocketChannel.class);
            }
            bootstrap.option(ChannelOption.SO_BACKLOG, nettyConfig.getBacklog());
            bootstrap.childHandler(nettyChannelInit);
            ChannelFuture channelFuture = bootstrap.bind(nettyConfig.getPort()).sync();
            channel = channelFuture.channel();
            String protocol = nettyConfig.isSsl() ? "wss" : "ws";
            String userEpollMessage = useEpoll ? "epoll模式" : "java nio模式";
            log.info("websocket服务器己启动, 启动模式:{}, 链接地址：{}://$[IP]:{} worker线程数：{}", userEpollMessage, protocol, nettyConfig.getPort() + nettyConfig.getWebsocketPath(), getWordCount());

            Runtime.getRuntime().addShutdownHook(new Thread(this::closeComponent));
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("WebSocket服务服务运行错误", e);
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public int getWordCount() {
        MultithreadEventLoopGroup workEventLoopGroup = (MultithreadEventLoopGroup) workerGroup;
        return workEventLoopGroup.executorCount();
    }

    private void closeComponent() {
        if (channel != null) {
            channel.close();
        }
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }

}
